package com.cgycms.prototype.impl;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.ZipUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cgycms.prototype.ProjectManagerService;
import com.cgycms.prototype.SitePrototypeService;
import com.cgycms.prototype.UserService;
import com.cgycms.prototype.cache.TimeCacheUtils;
import com.cgycms.prototype.common.BizException;
import com.cgycms.prototype.constant.ServiceCacheKeyConstant;
import com.cgycms.prototype.entity.PrototypeFileEntity;
import com.cgycms.prototype.entity.SiteEntity;
import com.cgycms.prototype.mapper.PrototypeFileMapper;
import com.cgycms.prototype.model.PrototypeFileModel;
import com.cgycms.prototype.model.UserModel;
import com.cgycms.prototype.model.UserTokenModel;
import com.cgycms.prototype.util.UserUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author: 超哥呦
 * @Description: 功能实现
 * @date: 2021/11/18 20:37
 **/
@Service
public class ProjectManagerServiceImpl extends ServiceImpl<PrototypeFileMapper, PrototypeFileEntity> implements ProjectManagerService {

    private Logger logger = LoggerFactory.getLogger(this.getClass());

    @Resource
    private SitePrototypeService sitePrototypeService;

    @Resource
    private PrototypeFileMapper prototypeFileMapper;

    @Resource
    private UserService userService;

    /**
     * 上传原型
     *
     * @param code          项目编号
     * @param multipartFile 文件
     * @param projectId     项目的主键id
     * @return
     * @throws IOException
     */
    @Override
    public Object projectUpload(String code, Long projectId, MultipartFile multipartFile) throws Exception {
        String originalFilename = multipartFile.getOriginalFilename();
        String extName = FileUtil.extName(originalFilename);
        if (!"zip".equals(extName)) {
            throw new BizException("您上传的文件类型错误，不是zip格式！");
        }

        //生成新原型编码
        String fileNewName = RandomUtil.randomString(6);
        if (prototypeFileMapper.queryNewFileName(fileNewName) > 0) {
            throw new BizException("系统出错啦，请重新上传！");
        }

        //上传原始文件
        SiteEntity siteEntity = sitePrototypeService.querySite();
        String diskName = siteEntity.getDiskName();
        File zipFile = FileUtil.mkdir(diskName + FileUtil.FILE_SEPARATOR + fileNewName + "." + extName);
        multipartFile.transferTo(zipFile);

        //解压文件
        File staticFile = FileUtil.mkdir(siteEntity.getZipOutName() + FileUtil.FILE_SEPARATOR + fileNewName);
        if (!unzip(zipFile, staticFile, Charset.defaultCharset(), 1)) {
            FileUtil.del(zipFile);
            FileUtil.del(staticFile);
            throw new BizException("解压失败，可能是已损坏或文件类型错误！");
        }

        //验证文件是否存在html，可以会有子目录文件夹套文件夹情况不允许上传。必须第一层目录就是原型html文件。
        List<File> files = Arrays.asList(FileUtil.ls(staticFile.getAbsolutePath()));
        Boolean checkFile = files.stream().map(a -> FileUtil.extName(a)).collect(Collectors.toSet()).contains("html");
        if (!checkFile) {
            FileUtil.del(staticFile);
            FileUtil.del(zipFile);
            throw new BizException("您上传的压缩包里不含html文件,请查看帮助文档重新创建压缩包形式！");
        }

        //入库
        Integer version = prototypeFileMapper.queryNewVersion(code);
        UserTokenModel userModel = UserUtils.getUser();
        PrototypeFileEntity entity = new PrototypeFileEntity();
        entity.setUnzipPath(staticFile.getAbsolutePath());
        entity.setUnzipSize(FileUtil.size(staticFile));
        entity.setZipSize(FileUtil.size(zipFile));
        entity.setCreateBy(userModel.getId().toString());
        entity.setCreateTime(DateUtil.date());
        entity.setDiskPath(zipFile.getAbsolutePath());
        entity.setFileNewName(fileNewName);
        entity.setOriginalName(originalFilename);
        entity.setPreviewUrl(siteEntity.getPreviewName() + "/" + fileNewName + "/index.html");
        entity.setUploadFileType(extName);
        entity.setCode(code);
        entity.setVersion(version == null ? 1 : version + 1);
        entity.setProjectId(projectId);
        entity.insert();
        TimeCacheUtils.remove(ServiceCacheKeyConstant.USER_PROJECT_KEY + code);
        TimeCacheUtils.remove(ServiceCacheKeyConstant.PROJECT_ID_KEY + projectId);
        logger.info("upload success! url:{},zip:{},disk:{}", entity.getPreviewUrl(), entity.getUnzipPath(), staticFile.getAbsolutePath());
        return entity;
    }

    @Override
    public List<PrototypeFileModel> selectProjectList(String projectCode) {
        if (ObjectUtil.isNull(projectCode)) {
            throw new BizException("查询原型信息出错,必填参数为空!");
        }
        List<PrototypeFileModel> resultList = prototypeFileMapper.selectProjectList(projectCode);
        Map<Long, UserModel> usersMap = userService.queryUserMap();
        for (PrototypeFileModel prototypeFileModel : resultList) {
            if (ObjectUtil.isNotNull(usersMap.get(Long.valueOf(prototypeFileModel.getCreateBy())))) {
                prototypeFileModel.setCreateByName(usersMap.get(Long.valueOf(prototypeFileModel.getCreateBy())).getNickName());
            }
        }
        return resultList;
    }

    @Override
    public Object projectDelete(Long id) {
        PrototypeFileEntity fileEntity = prototypeFileMapper.selectById(id);
        if (ObjectUtil.isNull(fileEntity)) {
            throw new BizException("未找到【" + id + "】原型信息！");
        }
        if (!FileUtil.exist(fileEntity.getDiskPath())) {
            throw new BizException("当前存储位置不是文件请联系管理员！");
        }
        String delPath = FileUtil.getAbsolutePath(fileEntity.getDiskPath());
        if (!FileUtil.isDirectory(fileEntity.getUnzipPath())) {
            throw new BizException("当前静态位置不是目录请联系管理员！");
        }
        String staticPath = FileUtil.getAbsolutePath(fileEntity.getUnzipPath());
        FileUtil.del(staticPath);
        FileUtil.del(delPath);
        prototypeFileMapper.deleteById(id);
        TimeCacheUtils.remove(ServiceCacheKeyConstant.PROJECT_ID_KEY + fileEntity.getProjectId());
        logger.info(delPath);
        return "删除成功!";
    }

    // 可能会出现问题，没有保证一致性，数据文件可能会存在不同步问题！
    @Override
    public Boolean projectDeleteByCode(String code) {
        if (ObjectUtil.isNull(code)) {
            throw new BizException("删除原型信息出错,必填参数为空!");
        }
        List<PrototypeFileModel> modelList = prototypeFileMapper.selectProjectList(code);
        for (PrototypeFileModel prototypeFileModel : modelList) {
            projectDelete(prototypeFileModel.getId());
        }
        return true;
    }

    @Override
    public PrototypeFileModel selectProjectByFileNewName(String code) {
        return prototypeFileMapper.selectProjectByFileNewName(code);
    }

    @Override
    public String getProjectNewUri(String code) {
        return prototypeFileMapper.getProjectNewUri(code);
    }

    /**
     * 解压压缩包 （治标不治本，有待改进。此处可以自己实现）
     *
     * @param zipFile    原文件
     * @param staticFile 解压后目录
     * @param charset    编码
     * @param index      重试次数
     * @return
     */
    private boolean unzip(File zipFile, File staticFile, Charset charset, Integer index) {
        try {
            ZipUtil.unzip(zipFile, staticFile, charset);
            logger.info("第{}次，解压文件成功!", index);
            return true;
        } catch (Exception e) {
            if (e.getMessage().contains("MALFORMED") && index < 2) {
                logger.warn("第{}次解压失败,更换编码解压再试一次！", index);
                return unzip(zipFile, staticFile, Charset.forName("GBK"), 2);
            }
            e.printStackTrace();
            logger.error("解压失败！{}", e.getMessage());
            return false;
        }
    }

}
